package com.xqboss.apps.service.sys;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xqboss.apps.domain.sys.Job;
import com.xqboss.apps.dto.sys.JobDto;
import com.xqboss.apps.enums.sys.JobConfigStateEnum;
import com.xqboss.apps.enums.sys.JobConfigTypeEnum;
import com.xqboss.apps.mapper.sys.JobMapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xqboss.apps.vo.sys.JobTypeVo;
import com.xqboss.common.exception.ServiceException;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.springframework.transaction.annotation.Transactional;

/**
 * <p>
 * 定时任务-按时间段配置 服务实现类
 * </p>
 *
 * @author 木鱼
 * @since 2024-03-11
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class JobService extends ServiceImpl<JobMapper, Job> {

    public List<JobTypeVo> selectBasicList() {
        List<Job> jobs = this.list();
        List<JobTypeVo> vos = new ArrayList<>();
        if (CollUtil.isNotEmpty(jobs)) {
            Map<JobConfigStateEnum, List<Job>> collect = jobs.stream().collect(Collectors.groupingBy(Job::getState));
            for (Map.Entry<JobConfigStateEnum, List<Job>> entry : collect.entrySet()) {
                JobConfigStateEnum key = entry.getKey();
                List<Job> value = entry.getValue();
                Map<JobConfigTypeEnum, List<Job>> collected = value.stream().collect(Collectors.groupingBy(Job::getType));
                JobTypeVo build = JobTypeVo.builder()
                        .state(key)
                        .jobName(key.getMsg())
                        .closeNum(collected.get(JobConfigTypeEnum.CLOSE)==null?0:collected.get(JobConfigTypeEnum.CLOSE).size())
                        .openNum(collected.get(JobConfigTypeEnum.OPEN)==null?0:collected.get(JobConfigTypeEnum.OPEN).size())
                        .expireNum(collected.get(JobConfigTypeEnum.EXPIRE)==null?0:collected.get(JobConfigTypeEnum.EXPIRE).size())
                        .build();
                vos.add(build);
            }
            if(collect.size()!= JobConfigStateEnum.values().length){
                for (JobConfigStateEnum value : JobConfigStateEnum.values()) {
                    if (!collect.containsKey(value)) {
                        JobTypeVo build = JobTypeVo.builder()
                                .state(value)
                                .jobName(value.getMsg())
                                .closeNum(0)
                                .openNum(0)
                                .expireNum(0)
                                .build();
                        vos.add(build);
                    }
                }
            }
        }else{
            for (JobConfigStateEnum value : JobConfigStateEnum.values()) {
                JobTypeVo build = JobTypeVo.builder()
                        .state(value)
                        .jobName(value.getMsg())
                        .closeNum(0)
                        .openNum(0)
                        .expireNum(0)
                        .build();
                vos.add(build);
            }
        }

        return vos;
    }

    public IPage<Job> selectBasicConfigList(JobDto<Job> dto) {
        return this.page(dto.newPage(), new LambdaQueryWrapper<Job>().eq(Job::getState,dto.getState()).orderByDesc(Job::getCreateTime));
    }

    @Transactional
    public void updateJobConfigState(Integer type,Long configId) {
        if (Objects.equals(type, JobConfigTypeEnum.CLOSE.getValue())) {
            Job job = this.getById(configId);
            Assert.notNull(job,()->new ServiceException("未查询到当前任务~"));
            Assert.isFalse(job.getIsUse(),()->new ServiceException("当前任务使用中，不能停止~"));
        }
        boolean update = this.lambdaUpdate().set(Job::getType, type).eq(Job::getId, configId).update();
        Assert.isTrue(update,()->new ServiceException("更新定时任务状态值失败~"));
    }

    @Transactional
    public void addOrEdit(Job record) {
        Assert.isFalse(DateUtil.compare(record.getJobOpenTime(),record.getJobCloseTime())<=0,()->new ServiceException("开启时间必须大于关闭时间~")) ;
        long MINUTE = DateUtil.between(record.getJobOpenTime(),record.getJobCloseTime(), DateUnit.MINUTE);
        Assert.isFalse(MINUTE/60<1,()->new ServiceException("时间段必须设置大于1小时~"));
        Assert.isFalse(MINUTE%60!=0,()->new ServiceException("时间段必须设置为整小时~"));
        boolean b = this.saveOrUpdate(record);
        Assert.isTrue(b,()->new ServiceException(record.getId()==null?"添加":"编辑"+"任务失败~"));
    }

    @Transactional
    public void removeBasic(Long id) {
        Job job = this.getById(id);
        Assert.notNull(job,()->new ServiceException("未查询到当前任务~"));
        Assert.isFalse(job.getIsUse(),()->new ServiceException("当前任务使用中，不能移除~"));
        boolean b =  this.removeById(id);
        Assert.isTrue(b,()->new ServiceException("移除任务失败~"));
    }

    /**
     * 当前时间之前的置为过期
     *
     */
    @Transactional
    public void setAsExpired(Date date) {
        List<Job> list = this.lambdaQuery().le(Job::getJobOpenTime, date).ne(Job::getType,JobConfigTypeEnum.EXPIRE).list();
        if (CollUtil.isNotEmpty(list)) {
            list.forEach(l->{
                l.setType(JobConfigTypeEnum.EXPIRE);
                l.setIsUse(false);
            });
            boolean b = this.saveOrUpdateBatch(list);
            Assert.isTrue(b,"定时任务置为过期失败~");
        }
    }

    /**
     * 判断当前空投时刻是否为- 空投时间
     *
     * @param stateEnum 任务类型
     * @param date 当前时间
     * @return true-空投时间
     */
    public Job isAirdrop(JobConfigStateEnum stateEnum, LocalDateTime date){
        Job job = this.lambdaQuery().eq(Job::getState,stateEnum).eq(Job::getType, JobConfigTypeEnum.OPEN).le(Job::getJobCloseTime, date).gt(Job::getJobOpenTime, date).last("limit 1").one();
        // 如果当前时间，能查询出数据，说明在这个时间段，配置了关闭/开启运动场 。 只要返回的数据不为空，就返回 false，表明为：停止空投时间段~
        if (ObjUtil.isNotEmpty(job)) {
            log.info("【当前时间为暂停时间段】：时间段名称：{} ，暂停打卡时间段：{} ~ {}， 为用户过期时间延后一个小时~",job.getName(),job.getJobCloseTime(),job.getJobOpenTime());
            if (!job.getIsUse()) {
                // 设置为该时间段正在使用中，不能够删除
                this.lambdaUpdate().set(Job::getIsUse,true).eq(Job::getId,job.getId()).update();
            }
            return job;
        }
        // 返回true，表明为空投时间，即后台没有当前时间 配置暂停力豆空投时间段
        return null;
    }
}